//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/lexer/lexx.h"
#include "sagittarius/lexer/lDeriv.h"
#include "sagittarius/lexer/dfa.h"
#include "sagittarius/lexer/sm.h"
#include "sagittarius/lexer/sm_c.h"
#include "sagittarius/io/io.h"
#include <stddef.h>
#include <malloc.h> 
#include <string.h>

uint32 lex_getLine(const LXAlphaType* buf, const LXAlphaType* p) {
    uint32 k=0;
    AT(p > buf);
    while(buf < p) {
        if(LX_CMPALPHA_EQ_SPC(*buf, '\n'))
            ++k;    
        ++buf;
    }
    return k+1;
}

lex_nodeLink* lex_addLink(lASTNode* n, lex_nodeLink* l) {
    lex_nodeLink* new = malloc(sizeof(lex_nodeLink));
    if(l)
        l->next = new;
    new->n = n;
    new->next = NULL;
    return new;
}

void lex_freeChain(lex_nodeLink* l) {
    //SG_WRITE(1, "lex_freeChain on\n");
    //lex_writeOut(2, l->n, 0);
    lAST_destroy_node(l->n);
    if(l->next)
        lex_freeChain(l->next);
    free(l);
}

void lex_setErrorFlag(lexWork* lw) {
    lw->error = 1;
}

void lex_sync(lexWork* lw) {
    while(!LX_CMPALPHA_EQ_SPC(*lw->ps.w, '\n') && !LX_CMPALPHA_EQ_SPC(*lw->ps.w, '\0'))
        ++lw->ps.w;
    if(LX_CMPALPHA_EQ_SPC(*lw->ps.w, '\n'))
        ++lw->ps.w;
}

void lex_errorAndSync(lexWork* lw) {
    lex_setErrorFlag(lw);
    lex_sync(lw);
}

size_t line_len(constLexBuf buf) {
    size_t l=1;
    constLexBuf b=buf;
    while(!LX_CMPALPHA_EQ_SPC(*b, '\n') && !LX_CMPALPHA_EQ_SPC(*b, '\0')) {
        ++l;
        ++b;
    }
    return l;
}

LexBuf substitute_line(lexWork* w, constLexBuf ln) {
    //reconstruct the RE from the AST node & stick it in
    return NULL;
}

void lex_pushVar(lexWork* lw, lexvar v) {
    lexvar* tmp;
    if(lw->var) {
        tmp = malloc((lw->nv+1)*sizeof(lexvar));
        memcpy(tmp, lw->var, lw->nv*sizeof(lexvar));
        free(lw->var);
        lw->var = tmp;
        lw->var[lw->nv++] = v;
    } else {
        lw->nv = 0;
        lw->var = malloc(sizeof(lexvar));
        lw->var[lw->nv++] = v;
    }
}

void lex_destroyVars(lexWork* lw) {
    size_t i;
    for(i=0; i<lw->nv; ++i) {
        LX_FREE_POOL_BUFFER(lw->var[i].name);
    }
    free(lw->var);
}

void lex_dumpVars(int fd, lexWork* lw) {
    size_t i;
    for(i=0; i<lw->nv; ++i) {
        SG_WRITE(2, "  ");
        LX_WRITE_BUF(fd, lw->var[i].name);
        SG_WRITE(2, "\n");
    }
}

/// init_lex
void init_lex(lexWork* lw, constLexBuf buf) {
    lex_initSymTab(&lw->tab);
    lw->ps = lex_createParserState(buf);
    lw->base = NULL;
    #if SAGITTARIUS_LXALPHA_USE_GMP
        lw->pi.emptychr.flg = 0;
    #endif
    LX_ALPHA_SET_SPC(lw->pi.emptychr, 0x1D700);
    
    lw->v = create_lexvec();
    lw->var = NULL;
}

/// finalize_lex
void finalize_lex(lexWork* lw) {
    lex_destroySymTab(&lw->tab);
    LX_ALPHA_CLEAR(lw->pi.emptychr);
    lex_destroyVars(lw);
    //lex_freeChain(lw->base);
    //lexvec destroyed with dfa
    //destroy_lexvec(lw->v);
}

lexWork* golex(constLexBuf buf) {
    #if !LX_USE_POOLALLOC
        //lex_nodeLink *l=NULL;
    #endif
    //lASTNode* n;
    lexvece* e;
    lexWork* lw = malloc(sizeof(lexWork));
    LexBuf line=NULL;
    constLexBuf rtn;
    size_t l;
    smrep* sm;
    //LXAlphaType* b;
    
    init_lex(lw, buf);
    lex_parseWS(&lw->ps, &lw->pi);
    
    while(!LX_CMPALPHA_EQ_SPC(*lw->ps.w, '\0')) {
        /*SG_WRITE(2, "Line: ");
        LX_WRITE_BUF(2, lw->ps.w);
        SG_WRITE(2, "\n");*/
        lex_parseWS(&lw->ps, &lw->pi);
        //parse comment
        if(lmatch(&lw->ps, '#')) {
            //SG_WRITE(2, "Parsed comment\n");
            lex_parseComment(&lw->ps, &lw->pi);
            lex_parseWS(&lw->ps, &lw->pi);
            continue;
        }
        
        //try to parse variable block
        if(l_trymatchstr(&lw->ps, "vars")) {
            lex_parseWS(&lw->ps, &lw->pi);
            while(!l_trymatchstr(&lw->ps, "end")) {
                //SG_WRITE(2, "buffer before:");
                //LX_WRITE_BUF(2, lw->ps.w);
                lex_pushVar(lw, lex_parseVar(&lw->ps, &lw->pi));
                lex_parseWS(&lw->ps, &lw->pi);
                //SG_WRITE(2, "loop");
            }
            continue;
        }
        
        l = line_len(lw->ps.w);
        if(l <= 1) {
            //AN(0);
            lex_parseWS(&lw->ps, &lw->pi);
            continue;
        }
        line = LX_CREATE_BUFFER(l);
        LX_COPY(line, lw->ps.w, l-1);
        LX_ALPHA_SET_SPC(line[l-1], '\0');
        rtn = lw->ps.w+l-1;
        lw->ps.w = line;
        
        //try to parse assignment
        e = lex_parseAssn(&lw->ps, &lw->pi);
        //lexvec_dumpElt(2, e, 0);
        if(!e) {
            SG_WRITE(2, "Failed to parse assignment on line \"");
            sg_writeUInt(2, lex_getLine(buf, lw->ps.w));
            SG_WRITE(2, "\n");
            lex_errorAndSync(lw);
            continue;
        }
        //consume whitespace
        //lex_parseWS(&lw->ps, &lw->pi);
        
        lxat_doSubstitutions(&e->r, &lw->tab);
        SG_WRITE(2, "Substituted:\n");
        lexvec_dumpElt(2, e, 0);
        lAST_canonicalize(&e->r);
        SG_WRITE(2, "Canonicalized:\n");
        lexvec_dumpElt(2, e, 2);
        
        //install in symbol table
        if(e->id) {
            if(lex_installSymbol(&lw->tab, e->id, e->r)) {
                SG_WRITE(2, "Unable to insert symbol \"");
                LX_WRITE_BUF(2, buf);
                SG_WRITE(2, "\"; already exists\n");
                lex_errorAndSync(lw);
                continue;
            }
        } else {
                SG_WRITE(2, "Malformed identifier at line \"");
                sg_writeUInt(2, lex_getLine(buf, lw->ps.w));
                SG_WRITE(2, "\n");
                lex_errorAndSync(lw);
                continue;
        }
        lexvec_push(lw->v, e);
        
        LX_FREE_BUFFER(line);
        lw->ps.w = rtn;
        //lex_parseWS(&lw->ps, &lw->pi);
    }
    
    lw->v2 = lexvec_elidePassive(lw->v);
    SG_WRITE(2, "Elided lexvec:\n");
    lexvec_dump(2, lw->v2, 0);
    
    if(lw->var) {
        SG_WRITE(2, "Variables:\n");
        lex_dumpVars(2, lw);
    }
    
	{
		UnicodeCChar* csrcbuf=NULL;
		dfarep* dfa = lex_makeDFAvec(lw->v2);
        if(dfa) {
            lex_dumpDFA(2, dfa);
        
            //construct sm
            sm = dfa2sm(dfa);
            
            //do C backend
            dfa2sm_c(&csrcbuf, sm, lw->var, lw->nv);
            //print C source
            sg_writeUStr(1, csrcbuf);
        
            free(csrcbuf);
            destroy_sm(sm);
            dfa_destroy(dfa);
        } else {
            SG_WRITE(2, "Failed to create DFA\n");
        }
	}
    
    SG_WRITE(2, "Freeing original vec\n");
    //lexvec v2 destroyed with dfa
    destroy_lexvec(lw->v);
    
    return lw;
}
